//
//  MorphologicalAnalyzer.h
//  LifeTracker_Test
//
//  Created by Petr Homola on 10/17/11.
//  Copyright (c) 2011 TapCrew. All rights reserved.
//

#ifndef LifeTracker_Test_MorphologicalAnalyzer_h
#define LifeTracker_Test_MorphologicalAnalyzer_h

#include "macros.h"
#include <string>
#include <vector>
#include <iostream>
#include <map>
#ifdef TR1
#include <tr1/unordered_map>
#include <tr1/memory>
#else
#include <unordered_map>
#include <memory>
#endif
#include "PhraseTable.h"
#include "Tokenizer.h"
#include <sstream>

namespace nlp {
    
    using namespace std;
#ifdef TR1
    using namespace std::tr1;
#endif
    
    class MorphologicalEntry {
    private:
        string lemma;
        string form;
        string tag;
        map<string,string> _avm;
    public:
        MorphologicalEntry(const string& l, const string& f, const string& t) : lemma(l), form(f), tag(t) {}
        const string& getLemma() const { return lemma; }
        const string& getForm() const { return form; }
        const string& getTag() const { return tag; }
        map<string,string>& avm() { return _avm; }
        map<string,string> avm() const { return _avm; }
    };
    
    class MorphologicalAnalyzer {
    protected:
        unordered_map<string,vector<MorphologicalEntry> > entries;
    public:
        const vector<MorphologicalEntry>* analyze(const string& form) {
            if (entries.find(form) != entries.end()) {
                auto& v = entries[form];
                return &v;
            } else {
                return nullptr;
            }
        }
        shared_ptr<PhraseTable> chart(const string& text) {
            auto chart = MAKESHARED(PhraseTable);
            {
                Tokenizer tokenizer(text);
                int i = 0;
                for (const auto& el : tokenizer.tokens()) {
                    auto* res = analyze(el);
                    if (res == nullptr) {
                        AVM* avm1 = new AVM();
                        avm1->put("pos", "?");
                        avm1->put("form", el);
                        avm1->put("lemma", el);
                        stringstream ss;
                        ss << i;
                        avm1->put("order", ss.str());
                        chart->add(new Phrase(shared_ptr<AVM>(avm1), i, i+ 1));
                    } else {
                        for (auto& entry : *res) {
                            AVM* avm1 = new AVM();
                            avm1->put("form", el);
                            avm1->put("lemma", entry.getLemma());
                            avm1->put("tag", entry.getTag());
                            stringstream ss;
                            ss << i;
                            avm1->put("order", ss.str());
                            for (auto it : entry.avm()) {
                                avm1->put(it.first, it.second);
                            }
                            chart->add(new Phrase(shared_ptr<AVM>(avm1), i, i+ 1));
                        }
                    }
                    i++;
                }
            }
            return chart;
        }
    };
    
    class EnglishMorphologicalAnalyzer : public MorphologicalAnalyzer {
    public:
        EnglishMorphologicalAnalyzer(const string& path) {
            FILE* file = fopen(path.c_str(), "rt");
            if (file != nullptr) {
                char line[1000];
                while (fgets(line, 1000, file) != nullptr) {
                    if (line[0] == '#' || line[0] == 0) continue;
                    char* sep = strchr(line, ' ');
                    *sep = 0;
                    char* end = strchr(sep + 1, '\n');
                    *end = 0;
                    string form = sep + 1;
                    sep = strchr(line, '/');
                    *sep = 0;
                    string tag = sep + 1;
                    string lemma = line;
                    auto& v = entries[form];
                    MorphologicalEntry entry(lemma, form, tag);
                    if (tag == "NN") {
                        entry.avm()["pos"] = "n";
                        entry.avm()["number"] = "sg";
                    }
                    if (tag == "NNS") {
                        entry.avm()["pos"] = "n";
                        entry.avm()["number"] = "pl";
                    }
                    if (tag == "JJ") {
                        entry.avm()["pos"] = "a";
                        entry.avm()["degree"] = "1";
                    }
                    if (tag == "JJR") {
                        entry.avm()["pos"] = "a";
                        entry.avm()["degree"] = "2";
                    }
                    if (tag == "JJS") {
                        entry.avm()["pos"] = "a";
                        entry.avm()["degree"] = "3";
                    }
                    if (tag == "VB") {
                        entry.avm()["pos"] = "v";
                        entry.avm()["vform"] = "inf";
                    }
                    if (tag == "DT") {
                        entry.avm()["pos"] = "det";
                    }
                    if (tag == "IN") {
                        entry.avm()["pos"] = "prep";
                    }
                    if (tag == "PRP") {
                        entry.avm()["pos"] = "pron";
                    }
                    if (tag == "RB") {
                        entry.avm()["pos"] = "adv";
                    }
                    v.push_back(entry);
                    //if (lemma == "book") cout << "'" << form << "' '" << lemma << "' '" << tag << "' " << v.size() << endl;
                }
                fclose(file);
                cout << "English morph. entries: " << entries.size() << endl;
            } else cout << "Cannot open morph. file" << endl;
        }
    };
    
}

#endif
